package configcenter

import (
	"cftgo/cftc"
	"cftgo/cftc/convert"
	"cftgo/cftf/configcenter"
	"cftgo/cftf/logger/syslog"
	"crypto/ecdsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"os"
	"strings"
)

type MysqlClusterConf struct {
	InstanceId   uint32 `json:"instance_id"`
	Host         string `json:"host"`
	Port         int32  `json:"port"`
	User         string `json:"user"`
	Passwd       string `json:"passwd"`
	DataBase     string `json:"database"`
	MaxOpenConns int32  `json:"max_open_conns"`
	MaxIdleConns int32  `json:"max_idle_conns"`
}

type LoginRedis struct {
	Host string `json:"host"`
	Port int32  `json:"port"`
}

type PublicKey struct {
	Kty string `json:"kty"`
	X   string `json:"x"`
	Y   string `json:"y"`
	Crv string `json:"crv"`
	Kid string `json:"kid"`
	Use string `json:"use"`
	Alg string `json:"alg"`
}

type config struct {
	loginDb        MysqlClusterConf
	loginRedis     LoginRedis
	privateKeyId   string
	privateKey     *ecdsa.PrivateKey
	allowLoginApps *cftc.ConcurrentMap[int32, int32]
	PublicKeys     *cftc.ConcurrentMap[string, PublicKey]
}

var instance = new(config)

func loadPrivateKeyFromPEM(pemStr string) (*ecdsa.PrivateKey, error) {
	// 解析PEM块
	block, _ := pem.Decode([]byte(pemStr))
	if block == nil {
		return nil, fmt.Errorf("无效的PEM格式")
	}

	// 解析EC私钥
	privateKey, err := x509.ParseECPrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("解析私钥失败: %v", err)
	}

	return privateKey, nil
}

func Init() {
	consulUrls := strings.Split(os.Getenv("ConsulUrls"), ";")
	if len(consulUrls) <= 0 || consulUrls[0] == "" {
		panic("consul_url not setting")
	}
	configcenter.SetConsulUrls(consulUrls)
	err, kvs := configcenter.GetValues("login", map[string]string{
		"recurse": "true",
	})
	{
		val := ""
		if configcenter.FindAndDecodeValueByPath(kvs, "login/db/accountdb", &val) {
			if cftc.DecodeJson(val, &instance.loginDb) == nil {
			} else {
				panic("configcenter parse logindb error")
			}
		} else {
			panic("configcenter read logindb error")
		}
	}
	{
		val := ""
		if configcenter.FindAndDecodeValueByPath(kvs, "login/redis/login", &val) {
			var cfg LoginRedis
			if cftc.DecodeJson(val, &cfg) == nil {
				instance.loginRedis = cfg
			} else {
				panic("configcenter parse loginRedis error")
			}
		} else {
			panic("configcenter read loginRedis error")
		}
	}
	{
		val := ""
		if configcenter.FindAndDecodeValueByPath(kvs, "login/private_key", &val) {
			if key, err := loadPrivateKeyFromPEM(val); err == nil {
				instance.privateKey = key
			} else {
				panic("configcenter parse private_key error")
			}
		} else {
			panic("configcenter read private_key error")
		}
	}
	if !readAppLoginSwitch() {
		panic("configcenter read app login switch error")
	}
	if !readPublishKeys() {
		panic("configcenter read publish keys")
	}
	syslog.Info("configcenter.init %s %s", err, kvs)
}

func UnInit() {

}

func readAppLoginSwitch() bool {
	err, kvs := configcenter.GetValues("apps/switch/login", map[string]string{
		"recurse": "true",
	})
	if err != nil {
		return false
	}
	appsSwitch := new(cftc.ConcurrentMap[int32, int32])
	for _, v := range kvs {
		ss := cftc.StrSplit(v.Key, "/")
		if len(ss) == 4 {
			if deVal, err := v.DecodeValue(); err == nil {
				appId := convert.ToInt32(ss[3])
				enable := convert.ToInt32(deVal)
				appsSwitch.Store(appId, enable)
			} else {
				return false
			}
		}
	}
	instance.allowLoginApps = appsSwitch
	return true
}

func readPublishKeys() bool {
	err, kvs := configcenter.GetValues("public/certs/jwks", map[string]string{
		"recurse": "true",
	})
	if err != nil {
		return false
	}
	keys := new(cftc.ConcurrentMap[string, PublicKey])
	reqOjb := struct {
		Keys []PublicKey `json:"keys"`
	}{}
	for _, v := range kvs {
		if v.Key == "public/certs/jwks" {
			if deVal, err := v.DecodeValue(); err == nil {
				if cftc.DecodeJson(deVal, &reqOjb) == nil {
					for _, keyObj := range reqOjb.Keys {
						keys.Store(keyObj.Kid, keyObj)
					}
				} else {
					return false
				}
			} else {
				return false
			}
		}
	}
	instance.PublicKeys = keys
	return true
}

func GetLoginDbConf() *MysqlClusterConf {
	return &instance.loginDb
}

func GetLoginRedisConf() *LoginRedis {
	return &instance.loginRedis
}

func GetPrivateKey() (string, *ecdsa.PrivateKey) {
	return "k1", instance.privateKey
}

func GetPublishKey(key string) *PublicKey {
	if pubKey, ok := instance.PublicKeys.Load(key); ok {
		return pubKey
	} else {
		return nil
	}
}

func AllowAppLogin(appId int32) bool {
	allowApps := instance.allowLoginApps
	if allowApps == nil {
		return false
	}
	enable, ok := allowApps.Load(appId)
	if !ok {
		return false
	}
	return *enable != 0
}
